// -----------------------------
// Author: 		Dhaenens Gert	
// Creation: 	2/15/2015 9:13:58 PM				
// -----------------------------

#include "Engine.h"

#include "Timer.h"
#include "GraphicsManager.h"
#include "StateManager.h"
#include "JobManager.h"
#include "FileManager.h"

#include "PlatformProvider.h"

#include "IGame.h"
#include "IPlatform.h"
#include "IWindow.h"
#include "IGraphicsDevice.h"

namespace Flow
{

	GameConfig			Engine::Config			= GameConfig();
	Color				Engine::ClearColor		= Color::Green;

	Timer*				Engine::GameTimer		= nullptr;
	StateManager*		Engine::States			= nullptr;
	JobManager*			Engine::Jobs			= nullptr;
	FileManager*		Engine::Files			= nullptr;

	IGame*				Engine::Game			= nullptr;
	IPlatform*			Engine::Platform		= nullptr;
	IWindow*			Engine::Window			= nullptr;

	Bool				Engine::ShouldRun		= true;

	GraphicsManager*	Engine::Graphics		= nullptr;

	Bool Engine::Initialize(IGame* lpGame)
	{
		ASSERT(lpGame);

		// Load the Platform Library
		if (!PlatformProvider::LoadPlatformLibrary())
			return false;

		Log::Initialize();

		// Setup the Default Config
		Config.Caption = "Flow";
		Config.Width = 800;
		Config.Height = 600;
		Config.Fullscreen = false;
		Config.VSync = true;

		// Save the game pointer
		// And query it's config
		Game = lpGame;
		Log::WriteLine("Querying Game Settings");
		Game->QuerySettings(Config);

		Log::WriteLine("Initializing Platform Resources");
		if (!InitializePlatformResources())
			return false;

		Log::WriteLine("Initializing Non-Platform Resources");
		if (!InitializeNonPlatformResources())
			return false;

		Log::WriteLine("Loading Engine Resource Package");
		if (!Files->LoadPackage(_T("EngineResources"), _T(".\\EngineResources.content")))
			return false;

		Log::WriteLine("********************************* INFO *********************************");
		Log::WriteLine("\tScreen Dimensions: %ux%u", Window->GetWidth(), Window->GetHeight());
		Log::WriteLine("************************************************************************");

		// Initialize the Game
		Log::WriteLine("Initializing Game");
		Game->OnInitialize();

		return true;
	}
	void Engine::Uninitialize()
	{
		Log::WriteLine("Uninitializing Engine");

		// Process all final jobs
		while (Jobs->HasJobsAvailable())
		{
			Jobs->ProcessJobs(ProcessOption::ProcessAll);
			Engine::Platform->Sleep(1);
		}

		// Wait for all jobs to finish
		Log::WriteLine("Waiting for Jobs to finish.");
		Jobs->WaitForJobs();

		SafeReleaseInterface(&Window);
		SafeReleaseInterface(&Platform);
		SafeReleaseInterface(&Game);

		delete Files;
		delete Jobs;
		delete States;
		delete Graphics;
		delete GameTimer;

		Log::Uninitialize();

		PlatformProvider::UnloadPlatformLibrary();
	}

	Bool Engine::InitializePlatformResources()
	{
		// Create the Platform Interface
		Platform = PlatformProvider::QueryInterface<IPlatform>("Platform");
		ASSERT(Platform);
		if (!Platform)
			return false;

		// Create the Window Interface
		Window = PlatformProvider::QueryInterface<IWindow>("Window");
		ASSERT(Window);
		if (!Window)
			return false;

		// Initialize the Window
		if (!Window->Initialize(Config))
			return false;

		return true;
	}

	Bool Engine::InitializeNonPlatformResources()
	{
		// Create the Timer
		GameTimer = new Timer();
		GameTimer->Reset();

		// Create the Graphics Manager
		Graphics = new GraphicsManager();
		ASSERT(Graphics != nullptr);
		if (!Graphics->Initialize(Window))
			return false;

		// Create the State Manager
		States = new StateManager();
		ASSERT(States != nullptr);

		// Create the File Manager
		Files = new FileManager();
		ASSERT(Files != nullptr);

		// Create the Job Manager
		Jobs = new JobManager();
		ASSERT(Jobs != nullptr);
		if (!Jobs->Initialize())
			return false;

		return true;
	}

	Int32 Engine::Run()
	{
		while (ShouldRun && Window->IsAlive())
		{
			if (Window->IsVisible())
			{
				// Update the Timer
				GameTimer->Update();
				Float deltaTime = GameTimer->GetDeltaTime();

				// Handle the message loop
				Window->HandleMessages(ProcessOption::ProcessAll);

				// Process the Job Manager
				Jobs->ProcessJobs(ProcessOption::ProcessAll);

				// Process the State Commands
				States->ProcessCommands(ProcessOption::ProcessAll);

				// Update the States
				States->Update(deltaTime);  

				// Draw a Frame
				Graphics->Draw();
			}
			else
			{
				// Handle the Remainder of the messages
				Window->HandleMessages(ProcessOption::ProcessOneAndAll);
			}
		}

		if (Window->IsAlive())
			Window->Close();

		return 0;
	}
	void Engine::Exit()
	{
		ShouldRun = false;
	}

}